package com.zjgsu.nftplatform.service.impl;

import cn.hutool.core.lang.Pair;
import cn.hutool.core.map.MapUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.zjgsu.nftplatform.entity.*;
import com.zjgsu.nftplatform.mapper.*;
import com.zjgsu.nftplatform.pojo.*;
import com.zjgsu.nftplatform.service.PaymentService;
import com.zjgsu.nftplatform.util.PaymentUtil;
import com.zjgsu.nftplatform.util.UserIdUtil;
import lombok.extern.log4j.Log4j2;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.apache.shiro.util.Assert;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.util.Map;

/**
 * @author ahy231
 * @date 2022/3/16 10:42
 * @description
 */
@Service
@Log4j2
public class PaymentServiceImpl implements PaymentService {

    private final PaymentMapper paymentMapper;
    private final UserMapper userMapper;
    private final PaymentUtil paymentUtil;

    @Value("${myurl.paymentUrl}")
    private String paymentUrl;
    @Value("${wechat.appid}")
    private String appid;
    @Value("${wechat.mchid}")
    private String mchid;
    @Value("${wechat.outTradeNoPrefix}")
    private String outTradeNoPrefix;
    @Value("${wechat.notifyUrl}")
    private String notifyUrl;

    public PaymentServiceImpl(PaymentMapper paymentMapper, UserMapper userMapper, PaymentUtil paymentUtil) {
        this.paymentMapper = paymentMapper;
        this.userMapper = userMapper;
        this.paymentUtil = paymentUtil;
    }

    @Override
    public String invoke(BigDecimal price) {
        long userId = UserIdUtil.getUserId();

        // 向数据库插入信息
        Payment payment = Payment.builder()
                .price(price)
                .userId(userId)
                .build();
        Assert.isTrue(paymentMapper.insert(payment) == 1, "数据库插入新的订单失败");
        Long paymentId = payment.getId();

        // 向微信支付请求订单
        PaymentRequest paymentRequest = PaymentRequest.builder()
                .appid(appid)
                .mchid(mchid)
                .description("微信支付购买商品")
                .out_trade_no(outTradeNoPrefix + paymentId)
//                .attach(paymentId.toString())
                .notify_url(notifyUrl)
                .amount(new Amount(price.multiply(BigDecimal.valueOf(100)).intValue()))
                .build();
        String body = JSON.toJSONString(paymentRequest);

        CloseableHttpClient payClient = paymentUtil.getPayClient();
        HttpPost httpPost = new HttpPost(paymentUrl);
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type","application/json; charset=utf-8");
        httpPost.setEntity(new StringEntity(body, "UTF-8"));

        try {
            CloseableHttpResponse httpResponse = payClient.execute(httpPost);
            String responseBody = EntityUtils.toString(httpResponse.getEntity());
            JSONObject jsonObject = JSON.parseObject(responseBody);

            return jsonObject.getString("code_url");
        } catch (Exception e) {
            throw new RuntimeException("支付失败，错误：" + e.getMessage());
        }
    }

    @Override
    public Map<String, String> callback(PayCallback callback) {
        // 解密resource
        PayResource resource = callback.getResource();
        String json;
        try {
            json = paymentUtil.decryptToString(
                    resource.getAssociated_data().getBytes(StandardCharsets.UTF_8),
                    resource.getNonce().getBytes(StandardCharsets.UTF_8),
                    resource.getCiphertext());
        } catch (GeneralSecurityException e) {
            e.printStackTrace();
            return MapUtil.<String, String>of(Pair.of("code", "FAIL"), Pair.of("message", "解析resource失败"));
        }
        JSONObject jsonObject = JSONObject.parseObject(json);

        log.info("resource: " + json);

        // 获取通知ID，判断是否已经处理过订单了
        String transactionId = jsonObject.getString("transaction_id");
        if (paymentMapper.selectByMap(MapUtil.<String, Object>of(Pair.of("order_id", transactionId), Pair.of("handled", true))).size() > 0) {
            // 处理过了，则直接返回
            log.info("忽略id为：{}的交易", transactionId);
            return MapUtil.<String, String>of(Pair.of("code", "SUCCESS"), Pair.of("message", "成功"));
        }

        // 通过out_trade_no修改商品支付状态
        String outTradeNo = jsonObject.getString("out_trade_no");
        Long paymentId = Long.valueOf(outTradeNo.replace(outTradeNoPrefix, ""));
        Payment payment = paymentMapper.selectById(paymentId);
        Assert.notNull(payment, "查找订单失败");
        Assert.isTrue(paymentMapper.updateById(Payment.builder()
                .id(paymentId)
                .orderId(transactionId)
                .build()) == 1, "更新交易信息失败");

        // 用户资产增加
        BigDecimal price = payment.getPrice().multiply(BigDecimal.valueOf(100));
        User user = userMapper.selectById(payment.getUserId());
        Assert.notNull(user, "查找不到支付用户：" + payment.getUserId());
        Assert.isTrue(userMapper.updateById(User.builder()
                .id(payment.getUserId())
                .assets(user.getAssets().add(price))
                .build()
        ) > 0, "更新用户资产失败");

        return MapUtil.<String, String>of(Pair.of("code", "SUCCESS"), Pair.of("message", "成功"));
    }
}
